﻿using Abp.Application.Services;
using Abp.Dapper.Repositories;
using Abp.Domain.Repositories;
using Batman.Cache;
using Batman.Cache.Attributes;
using Batman.Cms.Core.Const;
using Batman.Cms.Core.Models.Mvc;
using Batman.Cms.Core.Utils;
using Batman.Core;
using Batman.Jianbei.Forum.Areas.ChatRoom;
using Batman.Jianbei.Forum.Config;
using Batman.Jianbei.Forum.Core.ChatRoom;
using Batman.Jianbei.Forum.DTOS.Chatroom;
using Batman.Jianbei.Forum.Repositories.Mongo;
using Batman.Jianbei.Forum.Services.ChatRoom.CacheQs;
using Batman.Jianbei.Forum.Services.Users;
using Batman.Jianbei.Forum.SignalR;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Batman.Jianbei.Forum.Services.ChatRoom
{
    public class RoomService : ApplicationService, ICacheWorkable
    {
        public IRepository<TalkRoom, string> TalkRoomRepo { get; set; }

        public IRepository<ChatAction, string> chatActionRepo { get; set; }

        public IRepository<RoomMask, string> roomMaskRepo { get; set; }

        public IDapperRepository<ChatAction, string> chatActionDapperRepo { get; set; }

        public IHubContext<ChatHub> _hubContext { get; set; }

        public CountQueryCacheQs CountQueryCacheQs { get; set; }

        public IHostingEnvironment HostEnvironment { get; set; }

        /// <summary>
        /// 匿名身份分配服务
        /// </summary>
        public RoomMaskService roomMaskService { get; set; }

        public RoomMaskQueryService RoomMaskQueryService { get; set; }

        public MongoRepositoryBase<MsgHis> MsgHisCollection { get; set; }

        public RoomCacheQs RoomCacheService { get; set; }

        public MaskCacheService MaskCacheService { get; set; }

        public RoomService(IHostingEnvironment hostEnvironment)
        {
            HostEnvironment = hostEnvironment;
        }

        public RoleCacheService RoleCacheService { get; set; }

        public RoomCacheQs RoomCacheQs { get; set; }

        /// <summary>
        /// 创建聊天室
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [Cacher(CacherType.Clean, "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.RoomCacheQs.GetRoomList")]
        public ResponseBase Create(TalkRoom model, string uid)
        {
            var newRecord = TalkRoomRepo.Insert(model);
            roomMaskService.GetOrDistributeMask(uid, newRecord.Id, out var mask);

            // 发送到前端消息
            var createRoomMsg = new RoomCreateMsgDTO(newRecord.Id, mask, $"创建了房间", model.Name);
            _hubContext.Clients.All.SendAsync(createRoomMsg.GetMethod(), createRoomMsg);

            return new ResponseBase { Result = newRecord };
        }

        /// <summary>
        /// 创建聊天室（如果是管理员会调用这个方法，和上面的create方法里面的逻辑是一模一样的 主要是为了能清除9缓存）
        /// </summary>
        /// <param name="model"></param>
        ///// <returns></returns>
        [Cacher(CacherType.Clean, "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.RoomCacheQs.GetRoomList")]
        [Cacher(CacherType.Clean, TargetId = CacheNames.AdminRoomIds)]
        public virtual ResponseBase CreateAdminsRoom(TalkRoom model, string uid)
        {
            var newRecord = TalkRoomRepo.Insert(model);

            var distributedRoomMask = roomMaskRepo.GetQuery()
                                                    .Include(t => t.Mask)
                                                    .ThenInclude(t => t.Avatar)
                                                    .FirstOrDefault(t => t.RoomId == newRecord.Id && t.Uid == uid);

            var masks = MaskCacheService.GetMasks();
            var total = masks.Count();


            // 随机获取一个数据
            var randowm = new Random(DateTime.Now.Millisecond);
            var skip = randowm.Next(0, total - 1);
            var mask = masks.Skip(skip).FirstOrDefault();

            var newRoomMask = new RoomMask(newRecord.Id, mask.Id, uid, true);
            roomMaskRepo.Insert(newRoomMask); // 写入记录


            // 发送到前端消息
            var createRoomMsg = new RoomCreateMsgDTO(newRecord.Id, mask, $"创建了房间", model.Name);
            _hubContext.Clients.All.SendAsync(createRoomMsg.GetMethod(), createRoomMsg);

            return new ResponseBase { Result = newRecord };
        }

        /// <summary>
        /// 获取房间列表
        /// </summary>
        /// <returns></returns>
        public List<RoomItemDTO> GetRoomList()
        {
            var rooms = RoomCacheQs.GetRoomList();
            var roomsMemberCounts = CountQueryCacheQs.GetRoomsMemberCounts();

            // 转换成DTO数据
            var result = rooms.Select(t =>
            {
                var m = roomsMemberCounts.FirstOrDefault(s => s.Id == t.Id); // 按条找出房间的当前人数
                return new RoomItemDTO
                {
                    RoomId = t.Id,
                    RoomName = t.Name,
                    MemberCount = m == null ? 0 : m.Count
                };
            }).ToList();

            return result;
        }

        /// <summary>
        /// 获取房间资料
        /// </summary>
        /// <param name="roomId"></param>
        /// <returns></returns>
        public TalkRoom GetRoom(string roomId)
        {
            var query = TalkRoomRepo.GetQuery().Where(t => !t.IsDismiss && t.Id == roomId);

            return query.FirstOrDefault();
        }

        /// <summary>
        /// 加入房间
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="roomId"></param>
        /// <returns></returns>
        [Cacher(CacherType.Clean, TargetId = "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.CountQueryCacheQs.GetRoomsMemberCounts")]
        public ResponseBase Join(string uid, string roomId)
        {
            var room = GetRoom(roomId);

            if (room == null) return new ResponseBase(404, "房间已解散");

            var canJoin = room.CanJoin(chatActionRepo);
            if (!canJoin) return new ResponseBase(-1, "房间人数已满30人，暂时无法加入");

            var roomMask = roomMaskService.GetOrDistributeMask(uid, roomId, out var mask);
            if (roomMask.NotEntry) return new ResponseBase(-2, "您已被移出房间，不能加入");

            if (!roomMask.IsInRoom)
            {
                var enterMsg = new RoomEnterMsgDTO(roomId, mask, "进入房间");
                _hubContext.Clients.All.SendAsync(enterMsg.GetMethod(), enterMsg);
            }

            return new ResponseBase(true)
            {
                Result = new
                {
                    room,
                    mask,
                    isCreator = uid == room.CreateBy,                                           // 是否是创建人
                    isAdmin = RoleCacheService.GetAdminOrSupperAdminUids().Contains(uid)        // 是否是管理员
                }
            };
        }

        /// <summary>
        /// 离开房间(数据处理)
        /// </summary>
        /// <param name="room"></param>
        /// <param name="uid"></param>
        /// <returns></returns>
        [Cacher(CacherType.Clean, TargetId = "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.CountQueryCacheQs.GetRoomsMemberCounts")]
        public ResponseBase Leave(string roomId, string uid)
        {
            roomMaskService.MarkMaskLeave(roomId, uid, out var mask);

            if (mask == null) return new ResponseBase(true);

            var leaveMsg = new RoomLeaveMsgDTO(roomId, mask, "离开房间");
            // 发送广播
            _hubContext.Clients.All.SendAsync(leaveMsg.GetMethod(), leaveMsg);

            return new ResponseBase(true);
        }

        /// <summary>
        /// 获取聊天室成员信息
        /// </summary>
        /// <param name="roomId"></param>
        /// <param name="uid">当前用户ID</param>
        /// <returns></returns>
        public ResponseBase GetMembers(string roomId, string uid)
        {
            var members = roomMaskRepo.GetQuery().Include(t => t.Mask)
                                                 .ThenInclude(t => t.Avatar)
                                                 .Include(t => t.Room)
                                                 .Where(t => t.RoomId == roomId && t.IsInRoom)
                                                 .ToList();

            // 房间信息
            var roomInfo = RoomCacheService.GetRoomList().FirstOrDefault(t => t.Id == roomId);

            var currentUser = members.FirstOrDefault(t => t.Uid == uid); // 当前登录者的匿名身份

            return new ResponseBase
            {
                Result = new
                {
                    // 成员列表
                    members = members.Select(t => new
                    {
                        maskName = t?.Mask?.Nickname,
                        Avatar = t?.Mask?.Avatar?.Url,
                        MaskId = t?.Mask?.Id
                    }),
                    // 请求者身份
                    myMask = new
                    {
                        maskName = currentUser?.Mask?.Nickname,
                        Avatar = currentUser?.Mask?.Avatar?.Url,
                        MaskId = currentUser?.Mask?.Id
                    },
                    // 是否是管理员
                    isAdmin = RoleCacheService.GetAdminOrSupperAdminUids().Contains(uid),
                    // 是否是房间创建人
                    isCreator = currentUser?.Room?.CreateBy == uid,

                    room = roomInfo
                }
            };

        }

        /// <summary>
        /// 判断房间是否应该被销毁
        /// </summary>
        /// <param name="roomId"></param>
        private bool ShouldDestroy(string roomId)
        {
            var adminsRooms = RoomCacheService.GetAdministratorsRoomIds();
            if (adminsRooms.Contains(roomId)) return false; // 如果是管理员创建的房间，则不都不应该被销毁

            // 房间内的成员
            var memberInRoomCount = roomMaskRepo.GetQuery().Where(t => t.RoomId == roomId && t.IsInRoom).Count();
            return memberInRoomCount <= 1 ? true : false; // 房间内没人了则应该被销毁(当前退出人可能还在，没提交数据库事务)
        }

        /// <summary>
        /// 销毁房间
        /// </summary>
        /// <param name="roomId"></param>
        /// <param name="force">强制销毁（赶出所有成员）</param>
        /// <returns></returns>
        [Cacher(CacherType.Clean, "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.RoomCacheQs.GetRoomList")]
        public void Destroy(string roomId, bool force = false)
        {
            TalkRoomRepo.Update(roomId, t => t.IsDismiss = true);
            if (force)
            {
                var roomIdList = roomMaskRepo.GetQuery(false).Where(t => t.RoomId == roomId && t.IsInRoom).Select(t => t.Id).ToList();
                roomIdList.ForEach(t => roomMaskRepo.Update(t, s => s.IsInRoom = false));// 在线成员弄退出
            }

            var roomDestroyMsg = new RoomDestoryMsgDTO(roomId, new Mask(), "注销房间");
            // 发送注销广播
            _hubContext.Clients.All.SendAsync(roomDestroyMsg.GetMethod(), roomDestroyMsg);
        }

        public SendImgMQ SendImgMQ { get; set; }

        /// <summary>
        /// 发送图片消息
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="roomId"></param>
        /// <param name="serverId">上传到腾讯服务器的图片mediaId</param>
        public async Task<ResponseBase> SendPicMsg(string uid, string roomId, string serverId)
        {
            var roomMask = roomMaskRepo.GetQuery()
                                        .Include(t => t.Mask)
                                        .ThenInclude(t => t.Avatar)
                                        .FirstOrDefault(t => t.RoomId == roomId && t.Uid == uid);
            if (roomMask.NotEntry) return new ResponseBase(-2, "您已被移出聊天室");

            using (var mm = new MemoryStream())
            {
                Senparc.Weixin.MP.AdvancedAPIs.MediaApi.Get(WeixinSetting.weixin_app_id, serverId, mm);
                var savePath = ImageUtil.SaveImage(mm, ".jpg", HostEnvironment.ContentRootPath, out var success);
                if (!success)
                {
                    // todo: 写日志
                    return new ResponseBase(false, "发送失败!");
                }

                var mask = roomMask.Mask;
                var picMsgHis = new PicMsgHis(uid, roomId, mask, savePath);
                // 发送到MQ
                SendImgMQ.BasicPublish(picMsgHis);

                // 发送signalR消息
                var picMsgDTO = new RoomPicMsgDTO(roomId, mask, savePath);
                await _hubContext.Clients.All.SendAsync(picMsgDTO.GetMethod(), picMsgDTO);

                return new ResponseBase();
            }
        }

        /// <summary>
        /// 注销房间（主动注销）
        /// </summary>
        /// <param name="roomId"></param>
        /// <param name="uid"></param>
        [Cacher(CacherType.Clean, "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.RoomCacheQs.GetRoomList")]
        public ResponseBase Cancellation(string roomId, string uid)
        {
            var room = TalkRoomRepo.GetQuery().FirstOrDefault(t => t.Id == roomId && !t.IsDismiss);
            if (room == null) return new ResponseBase(false, "房间不存在或者已被注销");

            // 判断是不是聊天室作者 判断是不是管理员
            if (room.CreateBy != uid && !RoleCacheService.GetAdminOrSupperAdminUids().Contains(uid))
                return new ResponseBase(false, "您的权限不足，无法注销该聊天室");

            Destroy(roomId, true);
            return new ResponseBase();
        }

        /// <summary>
        /// 踢人出聊天室
        /// </summary>
        /// <param name="roomId">房间ID</param>
        /// <param name="maskId">要被踢的人的匿名身份ID</param>
        /// <param name="uid"></param>
        /// <returns></returns>
        [Cacher(CacherType.Clean, TargetId = "Batman.Jianbei.Forum.Services.ChatRoom.CacheQs.CountQueryCacheQs.GetRoomsMemberCounts")]
        public ResponseBase Kickout(string roomId, string maskId, string uid)
        {
            var isAdmins = RoleCacheService.GetAdminOrSupperAdminUids().Contains(uid); // 是否是管理员

            if (!isAdmins)
            {
                var isCreator = TalkRoomRepo.GetQuery().Where(t => t.Id == roomId).Select(t => t.CreateBy).FirstOrDefault() == uid;
                if (!isCreator) return new ResponseBase(false, "无权限");
            }

            // 找出要被踢的人
            var target = roomMaskRepo.GetQuery().Include(t => t.Mask).Where(t => t.RoomId == roomId && t.MaskId == maskId).FirstOrDefault();
            if (!target.NotEntry)
            {
                roomMaskRepo.Update(target.Id, t => { t.NotEntry = true; t.IsInRoom = false; }); // 更新数据库

                // 发送消息
                var kickoutMsg = new KickoutMsgDTO(roomId, target.Mask);
                _hubContext.Clients.All.SendAsync(kickoutMsg.GetMethod(), kickoutMsg);
            }

            return new ResponseBase();
        }
    }
}
